#include "Mailer.h"

CMailer::CMailer() { 
  memset(m_szUserName, '\0', USERNAME_MAX_LEN);
  memset(m_szPassWord, '\0', PASSWORD_MAX_LEN);
  memset(m_szUSRB64, '\0', USERNAME_MAX_LEN);
  memset(m_szPWDB64, '\0', PASSWORD_MAX_LEN);
  memset(m_szSmtp, '\0', SMTP_ADDR_LEN);
  memset(m_szFromAddr, '\0', MAIL_ADDR_LEN);
  memset(m_szBuf, '\0', BUFSIZ + 1);
  return;
}

CMailer::CMailer(const char* in_szSmtp) { 
  memset(m_szUserName, '\0', USERNAME_MAX_LEN);
  memset(m_szPassWord, '\0', PASSWORD_MAX_LEN);
  memset(m_szUSRB64, '\0', USERNAME_MAX_LEN);
  memset(m_szPWDB64, '\0', PASSWORD_MAX_LEN);
  memset(m_szSmtp, '\0', SMTP_ADDR_LEN);
  memset(m_szBuf, '\0', BUFSIZ + 1);
  memset(m_szFromAddr, '\0', MAIL_ADDR_LEN);
  strncpy(m_szSmtp, in_szSmtp, SMTP_ADDR_LEN - 1);
  return;
}

CMailer::~CMailer() {
  return;
}

int CMailer::set_smtp(const char* in_szSmtp) { 
  strncpy(m_szSmtp, in_szSmtp, SMTP_ADDR_LEN - 1);
  return 0;
}

int CMailer::set_user(const char* in_szUserName,
    const char* in_szPassWord) {
  get_user_name(in_szUserName, m_szUserName);

  strncpy(m_szFromAddr,
      in_szUserName,
      MAIL_ADDR_LEN);

  strncpy(m_szPassWord,
      in_szPassWord,
      PASSWORD_MAX_LEN - 1);

  Base64Encode(m_szUserName,
      m_szUSRB64,
      strlen(m_szUserName));

  Base64Encode(m_szPassWord,
      m_szPWDB64,
      strlen(m_szPassWord));
  return 0;
}

int CMailer::writen(const char* szBuf) {
  size_t len = strlen(szBuf);
  return writen(szBuf, len);
}

int CMailer::writen(const char* szBuf, size_t n) {           
  size_t nleft;
  ssize_t nwritten;
  nleft = n;
  while (nleft > 0) {
    if ((nwritten = write(m_nSockFd, szBuf, nleft)) < 0) {
      if (nleft == n) {
        return -1;
      }
      else {
        break;
      }
    }
    else if (nwritten == 0) {
      break;
    }
    nleft -= nwritten;
    szBuf +=nwritten;
  }
  return (n - nleft);
}        

int CMailer::readn()       
{  
  memset(m_szBuf, '\0', BUFSIZ);
  int len = read(m_nSockFd, m_szBuf, BUFSIZ);  
  if ( 0 >= len) {
    printf("Recv error or timeout!\n");
    return -1;
  }
  char szErrorCode[4] = {0};
  strncpy(szErrorCode, m_szBuf, 3);
  write(1, m_szBuf, len);
  int nErrorCode = atoi(szErrorCode);
  printf("Response Code = [%d]\n", nErrorCode);
  return nErrorCode;
}        

int CMailer::connect_server() {
  /*=====Create Socket=====*/  
  m_nSockFd = socket(AF_INET, SOCK_STREAM, 0);       
  if (-1 == m_nSockFd) {  
    perror("opening stream socket");  
    return -1;     
  }       
  else         
  {
    printf("socket created\n");  
  }

  /*=====Verify host=====*/  
  m_server.sin_family = AF_INET;  
  hp = gethostbyname(m_szSmtp);  

  if (hp == (struct hostent *)0) {  
    fprintf(stderr, "%s: unknown host\n", m_szSmtp);  
    return -1;  
  }  
  /*=====Connect to port 25 on remote host=====*/  
  memcpy((char *)&m_server.sin_addr, (char *)hp->h_addr, hp->h_length);                   

  m_server.sin_port=htons(25); /* SMTP PORT */  

  if (connect(m_nSockFd,(struct sockaddr *)&m_server, sizeof(m_server))==-1) {  
    perror("connecting stream socket");  
    return -1;  
  }       
  else {
    //cout << "Connected\n";  
    printf("Connected\n");  
  }
  return set_socket_timeout(); 
}

int CMailer::set_socket_timeout() {
  struct timeval timeout;
  memset(&timeout, 0x00, sizeof(struct timeval));
  timeout.tv_sec = SMTP_TIME_OUT;
  timeout.tv_usec = 0;
  return setsockopt(m_nSockFd,
                    SOL_SOCKET,
                    SO_RCVTIMEO,
                    (void *)&timeout,
                    sizeof(struct timeval));
}

int CMailer::login() {
    //connect server
    readn(); /* SMTP Server logon string */  
    char cl_ehlo[50] = {0};
    snprintf(cl_ehlo, 50, SMTP_CMD_EHLO, SMTP_SERVER);

    writen(cl_ehlo);
    readn(); /*Read reply */  
    //申请认证
    writen(SMTP_CMD_AUTH);
    readn();  
    //username:
    writen(m_szUSRB64);  //用户名的base64编码  
    writen(SMTP_CMD_LINE);
    readn();  
    //passwd
    writen(m_szPWDB64);  //密码的base64编码  
    writen(SMTP_CMD_LINE);
    if (235 != readn()) {
      printf("Login failed!\n");
      return -1;
    }
    printf("Login success!\n");
    return 0;
}

int CMailer::set_from(const char* in_szFrom) {
    //set sender
    char szFrom[128] = {0};
    snprintf(szFrom,
             128,
             SMTP_CMD_FROM,
             in_szFrom);
    writen(szFrom);
    if (250 != readn()) {
      return -1;
    }
    return 0;
}
int CMailer::set_rcpt(const char* in_szRcpt) {
  //set recipient
  char szRcpt[128] = {0};
  snprintf(szRcpt, 128, SMTP_CMD_RCPT, in_szRcpt);
  writen(szRcpt);
  if (250 != readn()) {
    return -1;
  }
  return 0;
}

int CMailer::init_mail() {
  //init mail
  writen(SMTP_CMD_DATA);
  if (354 != readn()) {
    return -1;
  }
  return 0;
}

int CMailer::quit_smtp() {
  //QUIT 
  writen(SMTP_CMD_QUIT);
  if (221 != readn())   {
    return -1;
  }
  return 0;
}

int CMailer::send_mail(const char* in_szAddr,
    const char* in_szSubject,
    const char* in_szContent,
    size_t in_nLen) {
  do
  {
    if (0 == strlen(m_szSmtp) ||
        0 == strlen(m_szUSRB64) ||
        0 == strlen(m_szPWDB64)) {
      printf("you must set smtp server and user!\n");
      break;
    }

    if (0 != connect_server()) {
      printf("connect error!\n");
      break;
    }
    if (0 != login()) {
      printf("login error!\n");
      break;
    }
    if (0 != set_from(m_szFromAddr)) {
      printf("set from!\n");
      break;
    }
    if (0 != set_rcpt(in_szAddr)) {
      printf("set rcpt!\n");
      break;
    }
    if (0 != init_mail()) {
      printf("init mail error!\n");
      break;
    }

    //send mail
    char szFrom[MAIL_ADDR_LEN] = {0};
    char szTo[MAIL_ADDR_LEN] = {0};
    snprintf(szFrom,
        MAIL_ADDR_LEN,
        "From:\"%s\"<%s>\r\n",
        m_szUserName,
        m_szFromAddr);

    snprintf(szTo, MAIL_ADDR_LEN, "To:<%s>\r\n", in_szAddr);
    writen(szFrom, strlen(szFrom));
    writen(szTo, strlen(szTo));

    //  主题
    char szSubject[SUBJECT_LEN] = {0};
    snprintf(szSubject, SUBJECT_LEN, "Subject:%s\r\n", in_szSubject);

    writen(szSubject);
    //  内容
    writen(in_szContent, in_nLen);  

    //mail over
    writen(SMTP_CMD_END);

    readn();

    if (0 != quit_smtp()) {
      printf("Quit error!\n");
      break;
    }
  } while(0);

  close(m_nSockFd);  
  return 0;
}

int CMailer::Base64Encode(char * OrgString,char * Base64String,int OrgStringLen) {
  static char Base64Encode[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  int Base64StringLen = 0;

  while(OrgStringLen > 0) {
    *Base64String ++=Base64Encode[(OrgString[0] >> 2) & 0x3f];
    if(OrgStringLen > 2) {
      *Base64String ++= Base64Encode[((OrgString[0] & 3) << 4) | (OrgString[1] >> 4)];
      *Base64String ++= Base64Encode[((OrgString[1] & 0xF) << 2) | (OrgString[2] >> 6)];
      *Base64String ++= Base64Encode[OrgString[2] & 0x3F];
    }
    else {
      switch(OrgStringLen) {
        case 1:
          *Base64String ++= Base64Encode[(OrgString[0] & 3) << 4];
          *Base64String ++= '=';
          *Base64String ++= '=';
          break;
        case 2:
          *Base64String ++= Base64Encode[((OrgString[0] & 3) << 4) | (OrgString[1] >> 4)];
          *Base64String ++= Base64Encode[((OrgString[1] & 0x0F) << 2)| (OrgString[2] >> 6)];
          *Base64String ++= '=';
          break;
      }
    }
    OrgString += 3;
    OrgStringLen -= 3;
    Base64StringLen += 4;
  }
  *Base64String = 0;
  return Base64StringLen;
}

int CMailer::get_user_name(const char* in_szAddr, char* out_szUser) {
  int i = 0;
  for(i = 0; i < strlen(in_szAddr); i++) {
    if('@' == in_szAddr[i]) {
      break;
    }
  }
  memcpy(out_szUser, in_szAddr, i);
  return 0;
}
